home *** CD-ROM | disk | FTP | other *** search
/ Programming Sound Cards / Programming Sound Cards.iso / sound_56 / dmastep7.asm < prev    next >
Assembly Source File  |  1995-01-01  |  17KB  |  448 lines

  1. ;══════════════════════════════════════════════════════════════════════════════
  2. ; Play 8bit DMA mode for SoundBlaster v2.00
  3. ;   André Baresel (with some help from Craig Jackson)
  4. ;──────────────────────────────────────────────────────────────────────────────
  5. ; STATUS: DOES WORK ON SB16/SBPRO2/SB2.0
  6. ;──────────────────────────────────────────────────────────────────────────────
  7. ; Requirements: 80286, SoundBlaster (see BASEADDR,DMA channel,IRQ number)
  8. ; Resolutions : 8-bit / 4..44khz (highspeed DMA)
  9. ; Parameters  : none
  10. ; Notes:
  11. ;     ■ pause/continue does only work on SB16 models because of highspeed DMA
  12. ;     ■ A new routine to create a DMAbuffer
  13. ;     ■ To creat a 8 bit mono unsigned file do :   "VOC2RAW TEST1.VOC /I"
  14. ;
  15. ; ■ DSP command 48h  ... set DMA block size
  16. ; ■ DSP command 90h  ... play highspeed DMA 8bit mono autoinit
  17. ; ■ DSP command 40h  ... set sample rate
  18. ; ■ DSP command D1h  ... Enable Speaker
  19. ; ■ DSP command D3h  ... Disable Speaker
  20. ; ■ DSP command D0h  ... Halt Autoinit 8 bit DMA operation
  21. ; ■ DSP command D4h  ... Continue Autoinit 8 bit DMA operation
  22. ;
  23.  
  24. .MODEL small
  25. .286
  26. ; CONSTANTS ───────────────────────────────────────────────────────────────────
  27.  
  28. ; SoundBlaster SETUP
  29. BASEADDR           EQU 0220h       ;SoundBlaster base address
  30. IRQ7               EQU 15          ;SoundBlaster IRQ
  31. DMAchannel         EQU 1           ;SoundBlaster DMA channel
  32.  
  33. ; PIC MASKS FOR MASK/DEMASK IRQ
  34. PICANDMASK         EQU 01111111b   ;'AND' PIC mask for clear IRQ7
  35. PICORMASK          EQU 10000000b   ;'OR' PIC mask for set IRQ7
  36.  
  37. ; DMA CONTROLLER REGISTERS :
  38. WRITEMASK          EQU 00ah         ;WRITE MASK REGISTER
  39. WRITEMODE          EQU 00bh         ;WRITE MODE REGISTER
  40. CLEARFLIPFLOP      EQU 00ch
  41. PAGE_CHN           EQU 083h         ;PAGE REGISTER FOR DMAchannel 1
  42. BASE_CHN           EQU 002h         ;BASEADDRESS REGISTER DMAchannel 1
  43. COUNT_CHN          EQU 003h         ;COUNT REGISTER DMAchannel 1
  44.  
  45. ; SAMPLERATE : (if you change it pay attention to maximum samplerate)
  46. TIMECONST          EQU 165          ; = 10989 Hz (256-1000000/11000)
  47.  
  48. ; DMA WRITE MODE
  49. WANTEDMODE         EQU 01011000b    ; singlemode, autoinit, readmode
  50.  
  51. ;──────────────────────────────────────────────────────────────────────────────
  52. ; MACRO DEFINITIONs
  53. ;──────────────────────────────────────────────────────────────────────────────
  54. STARTUP                 MACRO
  55. ; MASM 5.x COMPATIBILITY
  56. __start:                mov     ax,DGROUP
  57.                         mov     ds,ax
  58.                         mov     bx,ss
  59.                         sub     bx,ax
  60.                         shl     bx,004h
  61.                         mov     ss,ax
  62.                         add     sp,bx
  63. ENDM
  64.  
  65. WAITWRITE               MACRO
  66. LOCAL                   loopWait,endloop
  67. ;          Arguments : DX = Status port (BASEADDR+0Ch)
  68. ;          Returns   : n/a
  69. ;          Destroys  : AL
  70.  
  71.                         push    cx
  72.                         xor     cx,cx           ; need that for slow SBs !
  73. loopWait:               dec     cx
  74.                         jz      endloop
  75.                         in      al,dx           ; AL = WRITE COMMAND STATUS
  76.                         or      al,al
  77.                         js      loopWait        ; Jump if bit7=1 - writing not allowed
  78. endloop:                pop     cx
  79. ENDM
  80.  
  81. WAITREAD                MACRO
  82. LOCAL                   loopWait,endloop
  83. ;          Arguments : DX = Status port   (normaly BASEADDR+0Eh)
  84. ;          Returns   : n/a
  85. ;          Destroys  : AL
  86.  
  87.                         push    cx
  88.                         xor     cx,cx           ; need that for slow SBs !
  89. loopWait:               dec     cx
  90.                         jz      endloop
  91.                         in      al,dx           ; AL = DATA AVAILABLE STATUS
  92.                         or      al,al
  93.                         jns     loopWait        ; Jump if bit7=0 - no data available
  94. endloop:                pop     cx
  95. ENDM
  96.  
  97. RESET_DSP               MACRO
  98. local                   SBthere
  99. ;          Arguments : n/a
  100. ;          Returns   : n/a
  101. ;          Destroys  : DX,AL
  102.  
  103.                         mov      dx,BASEADDR+06h
  104.                         mov      al,1
  105.                         out      dx,al          ; start DSP reset
  106.  
  107.                         in       al,dx
  108.                         in       al,dx
  109.                         in       al,dx
  110.                         in       al,dx          ; wait 3 µsec
  111.  
  112.                         xor      al,al
  113.                         out      dx,al          ; end DSP Reset
  114.  
  115.                         add      dx,08h         ; dx = DSP DATA AVAILABLE
  116.                         WAITREAD
  117.                         sub      dx,4           ; dx = DSP Read Data
  118.                         in       al,dx
  119.                         cmp      al,0aah        ; if there is a SB then it returns 0AAh
  120.                         je       SBthere
  121.                         jmp      RESET_ERROR    ; No SB - exit program
  122. SBthere:
  123. ENDM
  124. ;─── End of Macrodefinitions ──────────────────────────────────────────────────
  125.  
  126. .STACK 100h
  127.  
  128. .DATA
  129. ;──────────────────────────────────────────────────────────────────────────────
  130.  
  131. SAMPLEBUFFER LABEL BYTE
  132.     INCLUDE TEST1.INC
  133. SAMPLEBUFFEREND LABEL BYTE
  134.  
  135.     part                db 1
  136.  
  137.     information         db 13,10,'DMASTEP7.EXE - DMA autoinit highspeed mode'
  138.                         db 13,10,'(the DMA init routine is different to DMASTEP6)'
  139.                         db 13,10,'Pause playing with key "p" and continue it then with any key (SB16+).'
  140.                         db 13,10,'Stop playing with <ESC>.','$'
  141.     memerror            db 13,10,'Not enough memory to creat the DMA buffer','$'
  142.     txtpart0            db 13,10,'playing part 0','$'
  143.     txtpart1            db 13,10,'playing part 1','$'
  144.     sberror             db 13,10,'No SoundBlaster at this BASEADDR ! PROGRAM HALTED.','$'
  145.  
  146.     OLDInterruptSEG     dw ?
  147.     OLDInterruptOFS     dw ?
  148.  
  149.     DMAbufferOFS        dw ?
  150.     DMAbufferPage       db ?
  151.  
  152.     SAMPLEBUFFERLENGTH = offset SAMPLEBUFFEREND - offset SAMPLEBUFFER
  153. ;──────────────────────────────────────────────────────────────────────────────
  154. .CODE
  155.  STARTUP
  156.  
  157.            ; FIRST FREE NOT USED MEMORY :
  158.            mov     bx,ss
  159.            mov     ax,es
  160.            sub     bx,ax
  161.  
  162.            mov     ax,sp
  163.            add     ax,15
  164.  
  165.            shr     ax,4
  166.  
  167.            add     bx,ax
  168.            mov     ah,04ah
  169.            int     21h
  170.  
  171.            ; NOW ALLOCATE DMABUFFER
  172.            mov     bx,SAMPLEBufferlength+15
  173.            shr     bx,3            ; cx = (samplebufferlength*2/16
  174.                                    ; (count of 16byte blocks)
  175.            mov     ah,48h
  176.            int     21h
  177.            jnc     enoughmem       ; ok got the memory
  178.            mov     dx,offset memerror
  179.            mov     ah,9
  180.            int     21h             ; WRITE MSG 2 SCRN THAT THERE'S NOT ENOUGH MEM
  181.            jmp     return2dos
  182. enoughmem: ; AX = segment of DMA buffer / offset = 0
  183.  
  184. ;──────────────────────────────────────────────────────────────────────────────
  185. ; calculate page and offset for DMAcontroller :
  186. ;
  187. ; segment*16+offset = 20bit memory location -> upper 4 bits  = page
  188. ;                                              lower 16 bits = offset
  189. ;──────────────────────────────────────────────────────────────────────────────
  190.            rol     ax,4
  191.            mov     bl,al
  192.            and     bl,00fh
  193.            and     al,0f0h
  194.            mov     [DMABufferOFS],ax
  195.            mov     [DMAbufferPage],bl
  196. ;──────────────────────────────────────────────────────────────────────────────
  197. ; check for DMApage override :
  198. ; ... problem: DMA controller separates memory into 64KB pages, you can only
  199. ; transfer data is placed in one page - no page overrides are allowed
  200. ;──────────────────────────────────────────────────────────────────────────────
  201. ; To solve that :
  202. ; creat a DMA buffer with double size you want - if the first part is placed
  203. ; on a page border the second part is for sure not
  204. ;──────────────────────────────────────────────────────────────────────────────
  205.            mov     cx,SAMPLEBUFFERLENGTH
  206.            neg     ax          ; ax = 65536 - ax   (bytes left to DMA page border)
  207.            cmp     ax,cx
  208.            ja      nooverride
  209.  
  210.            ; USE SECOND PART :
  211.            neg     ax               ; ax = offset first data
  212.            add     ax,cx            ; use second part
  213.            inc     ax             ; cx+1 bytes to next part ;)
  214.            mov     [DMABufferOFS],ax
  215.            inc     [DMABufferPage]  ; 2nd part is on next page !
  216. nooverride:
  217.  
  218. ;──────────────────────────────────────────────────────────────────────────────
  219. ; Now move the sampledata into the DMAbuffer
  220. ; in a MODplayer you'll do that in the IRQ,
  221. ; but in our case we only repeat the same sound in the DMAbuffer
  222. ;──────────────────────────────────────────────────────────────────────────────
  223.            ; FIRST CALCULATE THE DOS SEG/OFS FROM DMAPage/OFS
  224.            mov     al,byte ptr [DMABufferOFS]
  225.            and     al,0fh
  226.            xor     ah,ah
  227.            mov     di,ax       ; di = offset of DMAbuffer
  228.            mov     ax,[DMABufferOFS]
  229.            and     al,0f0h
  230.            or      al,[DMABufferPage]
  231.            ror     ax,4
  232.            mov     es,ax       ; es = segment of DMABuffer
  233.            mov     si,offset samplebuffer
  234.  
  235.            ;NOW COPY IT :
  236.            ; DS:SI - samples in dataseg
  237.            ; ES:DI - DMABuffer
  238.            ; CX - count of samples
  239.            ;
  240.            rep movsb
  241.  
  242.            RESET_DSP
  243.  
  244.            ; WRITE INFOMRATION TO SCREEN :
  245.            mov     dx,offset information
  246.            mov     ah,9
  247.            int     21h                  ; write program information to screen
  248.  
  249.            ; ENABLE SB SPEAKERS (for all SBs <SB16)
  250.            mov     dx,BASEADDR+00Ch            ;DX = DSP Write Data or Command
  251.            WAITWRITE
  252.            mov     al,0D1h                     ; AL = Enable speaker
  253.            out     dx,al                       ; Output: DSP Write Data or Command
  254.  
  255.            ; SETUP IRQ :
  256.            xor     ax,ax
  257.            mov     es,ax                       ; es to page 0 (Interrupt table)
  258.            mov     si,IRQ7*4                   ; si = position in interrupt table
  259.  
  260.            ; DISABLE IRQ (if it was enabled somehow)
  261.            in      al,021h
  262.            and     al,PICANDMASK               ; SET MASK REGISTER BIT TO DISABLE INTERRUPT
  263.            out     021h,al
  264.  
  265.            ; CHANGE POINTER IN INTERRUPT TABLE
  266.            mov     ax,es:[si]
  267.            mov     [OLDInterruptOFS],ax        ; save offset of old interupt vector for restoring
  268.            mov     ax,OFFSET OWN_IRQ
  269.            mov     es:[si],ax                  ; set offset of new interrupt routine
  270.            mov     ax,es:[si+2]
  271.            mov     [OLDInterruptSEG],ax        ; save segment of old interupt vector for restoring
  272.            mov     ax,cs
  273.            mov     es:[si+2],ax                ; set segment of new interrupt routine
  274.  
  275.            ; CHANGE PIC MASK :
  276.            in      al,021h
  277.            and     al,PICANDMASK   ; CLEAR MASK REGISTER BIT TO ENABLE INTERRUPT
  278.            out     021h,al
  279.  
  280.  
  281.            mov     cx,SAMPLEBUFFERLENGTH-1
  282. ;──────────────────────────────────────────────────────────────────────────────
  283. ; Setup DMA-controller :
  284. ;
  285. ; 1st  MASK DMA CHANNEL
  286. ;
  287.            mov     al,DMAchannel
  288.            add     al,4
  289.            out     WRITEMASK,al
  290. ;──────────────────────────────────────────────────────────────────────────────
  291. ; 2nd  CLEAR FLIPFLOP
  292. ;
  293.            out     CLEARFLIPFLOP,al
  294. ;──────────────────────────────────────────────────────────────────────────────
  295. ; 3rd  WRITE TRANSFER MODE
  296. ;
  297.            mov     al,WANTEDMODE
  298.            add     al,DMAchannel
  299.            out     WRITEMODE,al
  300. ;──────────────────────────────────────────────────────────────────────────────
  301. ; 4th  WRITE PAGE NUMBER
  302. ;
  303.            mov     al,[DMAbufferPage]
  304.            out     PAGE_CHN,al
  305. ;──────────────────────────────────────────────────────────────────────────────
  306. ; 5th  WRITE DMA BASEADDRESS
  307. ;
  308.            mov     ax,[DMABufferOFS]
  309.            out     BASE_CHN,al
  310.            mov     al,ah
  311.            out     BASE_CHN,al
  312. ;──────────────────────────────────────────────────────────────────────────────
  313. ; 6th  WRITE BASECOUNTER = SAMPLELENGTH-1
  314. ;
  315.            mov     al,cl
  316.            out     COUNT_CHN,al
  317.            mov     al,ch
  318.            out     COUNT_CHN,al
  319. ;──────────────────────────────────────────────────────────────────────────────
  320. ; 7th  DEMASK CHANNEL
  321. ;
  322.            mov     al,DMAchannel
  323.            out     WRITEMASK,al
  324.  
  325. ;──────────────────────────────────────────────────────────────────────────────
  326. ; Setup SoundBlaster :
  327. ;
  328. ; 1st  SET TIMECONSTANT
  329. ;
  330.            mov     dx,BASEADDR+00Ch            ;DX = DSP Write Data or Command
  331.            WAITWRITE
  332.            mov     al,040h                     ;AL = Set timeconstant
  333.            out     dx,al
  334.            WAITWRITE
  335.            mov     al,TIMECONST
  336.            out     dx,al
  337. ;──────────────────────────────────────────────────────────────────────────────
  338. ; 2nd  USE HIGHSPEED,autoinit DMA (DSPC 90h)
  339. ;
  340.            ; FIRST SETUP DMA-BUFFERSIZE
  341.            WAITWRITE
  342.            mov     al,048h                     ;AL = DMA DAC 8bit
  343.            out     dx,al
  344.            mov     cx,SAMPLEBUFFERLENGTH
  345.            shr     cx,1                        ; generate IRQ every half buffer
  346.            dec     cx
  347.            WAITWRITE
  348.            mov     al,cl                       ;AL = LOWER PART SAMPLELENGTH
  349.            out     dx,al
  350.            WAITWRITE
  351.            mov     al,ch                       ;AL = HIGHER PART SAMPLELENGTH
  352.            out     dx,al
  353.  
  354.            ; SETUP PLAYMODE
  355.            WAITWRITE
  356.            mov     al,90h
  357.            out     dx,al
  358.  
  359. ; TRANSFER STARTs.....NOW..... :)
  360.  
  361. waitloop:  mov     ah,01                       ;AH = Check for character function
  362.            int     016h                        ;   Interrupt: Keyboard
  363.            jz      waitloop                    ; wait for a key (sound in background)
  364.  
  365.            xor     ah,ah                       ;Read character, flush keypress
  366.            int     016h                        ;   Interrupt: Keyboard
  367.            cmp     al,'p'                      ; check for pause key
  368.            je      pause                       ; ok halt playing
  369.            cmp     al,27
  370.            jne     waitloop
  371.            jmp     exit
  372. pause:     ; NOW HALT IT :
  373.            mov     dx,BASEADDR+00Ch            ;DX = DSP Write Data or Command
  374.            WAITWRITE
  375.            mov     al,0D0h
  376.            out     dx,al
  377.  
  378.            ;WAIT FOR ANY KEY (what's the 'ANY' key ?)
  379.            xor     ah,ah                       ;Read character, flush keypress
  380.            int     016h                        ;   Interrupt: Keyboard
  381.  
  382.            ; NOW CONTINUE PLAYING
  383.            mov     dx,BASEADDR+00Ch            ;DX = DSP Write Data or Command
  384.            WAITWRITE
  385.            mov     al,0d4h
  386.            out     dx,al
  387.  
  388.            jmp     waitloop
  389.  
  390. exit:      RESET_DSP
  391.  
  392.            ; RESTORE PIC MASK
  393.            in      al,021h
  394.            or      al,PICORMASK                ;<-- SET REGISTER MASK BITS TO DISABLE
  395.            out     021h,al
  396.  
  397.            ; RESTORE IRQ :
  398.            xor     ax,ax
  399.            mov     es,ax                       ; es to page 0 (Interrupt table)
  400.            mov     si,IRQ7*4
  401.            mov     ax,[OLDInterruptOFS]
  402.            mov     es:[si],ax                  ; set old interrupt routine
  403.            mov     ax,[OLDInterruptSEG]
  404.            mov     es:[si+2],ax
  405.  
  406.            ; CLEAR KEYBUFFER
  407.            mov     ah,01
  408.            int     16h
  409.            jz      return2dos
  410.            xor     ah,ah                       ;Read character, flush keypress
  411.            int     016h                        ;   Interrupt: Keyboard
  412.  
  413.            ; TERMINATE EXE:
  414. return2dos:
  415.            mov     ax,04c00h
  416.            int     21h
  417.  
  418. ; display information if Soundblaster is not on this baseaddress
  419. RESET_ERROR:
  420.            mov     dx,offset sberror
  421.            mov     ah,9
  422.            int     21h                         ; text output
  423.            jmp     return2dos
  424.  
  425. ;──────────────────────────────────────────────────────────────────────────────
  426. ; Our own IRQ for detecting buffer half SB currently plays
  427. ; It's generated by the SoundBlaster hardware
  428. ;──────────────────────────────────────────────────────────────────────────────
  429. OWN_IRQ:   pusha
  430.            mov     dx,BASEADDR+00Eh            ;DX = DSP DATA AVAILABLE (IRQ ACKNOWLEDGE)
  431.            in      al,dx
  432.            mov     ax,@data
  433.            mov     ds,ax
  434.            mov     dx,offset txtpart0
  435.            cmp     [part],0
  436.            je      notpart1
  437.            mov     dx,offset txtpart1
  438. notpart1:  mov     ah,9
  439.            int     21h             ; text output
  440.            neg     [part]
  441.            inc     [part]          ; part = 1-part  result : 0,1,0,1,0,....
  442.            mov     al,020h
  443.            out     020h,al                     ;ACKNOWLEDGE HARDWARE INTERRUPT
  444.            popa
  445.            IRET
  446.  
  447. END     __start
  448.